/*-----------------------------------------------------------------------------
 *
 *  eventlog.c - Windows NT eventlog module
 *
 *    Copyright (c) 1998-1999, SaberNet.net - All rights reserved
 *
 *    This program is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU General Public License
 *    as published by the Free Software Foundation; either version 2
 *    of the License, or (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307
 *
 *  $Id: eventlog.c,v 1.1.1.1 2001/11/28 05:25:04 sabernet Exp $
 *
 *  Revision history:
 *    15-Oct-1999  JRR  Fixed handling of events w/ missing descriptions
 *    06-Jul-1998  JRR  Module completed
 *
 *----------------------------------------------------------------------------*/
#include "windows.h"
#include <time.h>
#include <stdio.h>
#include "ntsl.h"
#include "eventlog.h"
#include "engine.h"
#include "service.h"

static int  eventlog_system       = 0;
static int  eventlog_security     = 0;
static int  eventlog_application  = 0;

/*-------------------------------[ static data ]------------------------------*/
#define REG_BUFFER_LEN      2048
#define MAX_MSG_STRINGS      100	// FormatMessage(): %n = {1..99}
#define LAST_RUN_REG       "LastRun"

#define EVENTLOG_INFORMATION_FLAG	0x1
#define EVENTLOG_WARNING_FLAG		0x2
#define EVENTLOG_ERROR_FLAG			0x4
#define EVENTLOG_AUDIT_SUCCESS_FLAG	0x8
#define EVENTLOG_AUDIT_FAILURE_FLAG	0x10


/*----------------------------[ private functions ]---------------------------*/
static char  *eventlog_lcase(char *s);
static int    eventlog_append_data(char *s, int len, char* data, BOOL chop);
static char **eventlog_strings_to_array(char *strings, int num);
static int    eventlog_read_events(int* eventlog, char *logType, uint32 *lastrun, uint32 *thisrun);
static int    eventlog_read_lastrun(uint32 *lastrun);
static int    eventlog_write_lastrun(uint32 *lastrun);
static int    eventlog_set_event_type(ntsl_event *event, int id);



/*-----------------------------[ eventlog_lcase ]-----------------------------
 * Convert string to lowercase
 *----------------------------------------------------------------------------*/
static char *eventlog_lcase(char *s)
{
	char *sp = s;

    // DBCS
	//while(*sp) { *sp = tolower(*sp); sp++; }
	while(*sp) 
	{ 
		if (!(IsDBCSLeadByte(*sp)))
		{
			*sp = tolower(*sp); 
			sp++;
		}
		else
			CharNext(sp);
	}
		
	return(s);
} 


/*--------------------------[ eventlog_append_data ]--------------------------
 * Appends up to n bytes of data to the end of the buffer. (Null terminating) 
 *
 *	Returns:
 *		success		0
 *		failure		-1 
 *----------------------------------------------------------------------------*/
static int eventlog_append_data(char *buffer, int n, char *data, BOOL chop)
{
	int	rc = -1;

	if ( (buffer != NULL) && (data != NULL) )
	{
		int i  = strlen(buffer);
		int	nl = 0;

		if (chop)
			while(iswspace(*data)) data++;

		while (*data)
		{
			// DBCS
			if (IsDBCSLeadByte(*data))
			{
				if (i < (n - 2))
					buffer[i++] = *data++;
					buffer[i++] = *data;
			}
			// DBCS
			//if (isprint(*data) || *data == '\n')
			else if (isprint(*data) || *data == '\n')
			{
				if (*data == '\n') 
					*data = ' '; 
				if (i < (n - 1))	
					buffer[i++] = *data;
			}
			data++;
		}
		buffer[i] = (char)0;
		rc = 0;
	}

	return(rc);
}

/*------------------------[ eventlog_strings_to_array ]------------------------
 * Converts a concatenation of null terminated strings to an array of strings.
 * (Null terminating)
 *----------------------------------------------------------------------------*/
static char **eventlog_strings_to_array(char *strings, int num)
{
	static char *array[MAX_MSG_STRINGS];
	int			 i;

	if (strings == NULL)
	{
		array[0] = NULL;
		return(array);
	}

	if (num > MAX_MSG_STRINGS)
		num = MAX_MSG_STRINGS;

	for(i=0; i<num; i++)
	{
		array[i] = strings;
		strings += strlen(strings) + 1;
	}
	array[i] = NULL;

	return(array);
}

/*--------------------------[ eventlog_check_event ]--------------------------
 * Returns non-zero value if interested in the event; otherwise returns 0
 *----------------------------------------------------------------------------*/
static int eventlog_check_event(int *eventlog, int id)
{
	int rc = 0;

	switch(id)
	{
		case EVENTLOG_ERROR_TYPE:		
				rc = (*eventlog & EVENTLOG_ERROR_FLAG);
				break;
		case EVENTLOG_WARNING_TYPE:     
				rc = (*eventlog & EVENTLOG_WARNING_FLAG);  
				break;
		case EVENTLOG_AUDIT_FAILURE:    
				rc = (*eventlog & EVENTLOG_AUDIT_FAILURE_FLAG);
			    break;
		case EVENTLOG_AUDIT_SUCCESS:    
				rc = (*eventlog & EVENTLOG_AUDIT_SUCCESS_FLAG);     
				break;
		case EVENTLOG_INFORMATION_TYPE: 
				rc = (*eventlog & EVENTLOG_INFORMATION_FLAG);     
				break;
	}
	return(rc);
}

/*-------------------------[ eventlog_set_event_type ]-------------------------
 * Sets the type for the given event.
 *----------------------------------------------------------------------------*/
static int eventlog_set_event_type(ntsl_event *event, int id)
{
	int   rc    = -1;
	char *eType = NULL;

	if (NULL != event)
	{
		switch (id)
		{
			case EVENTLOG_ERROR_TYPE:		eType = NTSL_EVENT_ERROR;	     break;
			case EVENTLOG_WARNING_TYPE:     eType = NTSL_EVENT_WARNING;      break;
			case EVENTLOG_INFORMATION_TYPE: eType = NTSL_EVENT_INFORMATION;  break;
			case EVENTLOG_AUDIT_SUCCESS:    eType = NTSL_EVENT_SUCCESS;      break;
			case EVENTLOG_AUDIT_FAILURE:    eType = NTSL_EVENT_FAILURE;      break;
		};
		
		if (eType != NULL)
		{
			strncpy0(event->etype,  eType, NTSL_SYS_LEN);	
			rc = 0;
		}
	}

	return(rc);
}


/*-------------------------[ eventlog_set_event_msg ]-------------------------
 * Retrieves the event message from the appropriate DLL.
 *
 *	Returns:
 *		success		0
 *		failure		-1 
 *----------------------------------------------------------------------------*/
static int eventlog_set_event_msg(ntsl_event *event, char *logType,
										uint32 id, char *strings, int numStrings)
{
	char	**array; 
	char	buffer[REG_BUFFER_LEN];
	char	dll[REG_BUFFER_LEN];
	uint32	bufsize = REG_BUFFER_LEN;
	uint32	bytes   = 0;
	uint32	regtype = 0;
	HANDLE	hkey, hlib;
	char	eventID[REG_BUFFER_LEN];

	/* check paramaters */
	if ( (event == NULL) || (logType == NULL) || (strings == NULL) )
		return(-1);

	array = eventlog_strings_to_array(strings, numStrings);

	/* check strings array */
	if ( (numStrings) && (array[0] == NULL) )
		return(-1);

	/* build registry path */
	_snprintf0(buffer, REG_BUFFER_LEN, 
		"SYSTEM\\CurrentControlSet\\Services\\EventLog\\%s\\%s", logType,
		 event->source);

	/* add event ID to the msg */
	_snprintf0(eventID, REG_BUFFER_LEN, "EventID: 0x%02x ", id);
	event->msg[0] = (char)0;
	eventlog_append_data(event->msg, NTSL_EVENT_LEN, eventID, FALSE);

	/* load message text */
	if (RegOpenKey(HKEY_LOCAL_MACHINE, buffer, &hkey) == ERROR_SUCCESS)
	{
		if (RegQueryValueEx(hkey, "EventMessageFile", 0, &regtype, 
				(unsigned char*)buffer, &bufsize) == ERROR_SUCCESS)
		{
			if (ExpandEnvironmentStrings(buffer, dll, REG_BUFFER_LEN) > 0)
			{
				if ((hlib = LoadLibraryEx(dll, NULL, DONT_RESOLVE_DLL_REFERENCES)) != NULL)
				{
					LPCVOID msg  = NULL;
					
					bytes = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
						 			FORMAT_MESSAGE_FROM_HMODULE |
									FORMAT_MESSAGE_ARGUMENT_ARRAY | 60, hlib, id, 
								// DBCS
								//MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 
								MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT), 
								(LPTSTR)&msg, NTSL_EVENT_LEN, array);
	
					if (bytes > 0)
					{
						eventlog_append_data(event->msg, NTSL_EVENT_LEN, (char *)msg, TRUE);
						LocalFree((HANDLE)msg);
					}

					FreeLibrary(hlib);
				}
			}
		}
		RegCloseKey(hkey);
	}

	if (bytes > 0)
	{
			; // handled above
	}
	else if (numStrings > 0)
	{
		int i;
						
		for (i=0; i<numStrings; i++)
		{
			eventlog_append_data(event->msg, NTSL_EVENT_LEN, array[i], FALSE);
			eventlog_append_data(event->msg, NTSL_EVENT_LEN, " ", FALSE);
		}
	}
	else
	{
		eventlog_append_data(event->msg, NTSL_EVENT_LEN, "No description available", FALSE);
	}

	return(0);
}

/*--------------------------[ eventlog_read_events ]--------------------------
 * Read messages from the event log 
 *
 *	Returns:
 *		success		(0)
 *		error		(-1)	
 *----------------------------------------------------------------------------*/
static int eventlog_read_events(int *eventlog, char *logType, uint32 *lastrun,
	uint32 *thisrun)
{
    char                buffer[REG_BUFFER_LEN];
    int                 useTimeStamp = 1;
    uint32              bytes;
    uint32              next;
    EVENTLOGRECORD      *record = (EVENTLOGRECORD *)&buffer;
    HANDLE              hLog;

	if ( (logType == NULL) ||
	     (lastrun == NULL) || (thisrun == NULL)   )
		return(-1);

    if ((hLog = OpenEventLog(NULL, logType)) == NULL) 
	{
		ntsl_log_error(NTSL_ERROR_EVENT_LOG_ACCESS, logType);
		return(-1);
	}


    while (ReadEventLog(hLog, EVENTLOG_FORWARDS_READ | EVENTLOG_SEQUENTIAL_READ,
			 0, record, REG_BUFFER_LEN, &bytes, &next))   
    {
		// check for service shutdown
		if (service_halting())
			return(-1);

        while (bytes > 0) 
        { 
			ntsl_event	*event;
			char		*source   = (LPSTR) ((LPBYTE) record + sizeof(EVENTLOGRECORD));
			char		*computer = source + strlen(source) + 1;
			char		*strings  = (LPSTR) ((LPBYTE) record + record->StringOffset);
			struct tm   *time     = localtime(&record->TimeGenerated);

			if ( (record->TimeWritten >  (uint32)*lastrun) && 
                 (record->TimeWritten <= (uint32)*thisrun) &&
				 (eventlog_check_event(eventlog, record->EventType)) )
			{
				if ( (event = malloc0(sizeof(ntsl_event))) == NULL)
				{
					ntsl_log_error(NTSL_ERROR_EVENT_MALLOC);
					return(-1);
				}

				event->msg[0] = 0;
				strftime(event->date, NTSL_DATE_LEN, "%b %d %H:%M:%S", time);
				if (event->date[4] == '0') // Unix style formatting
					event->date[4] = ' ';
				strncpy0(event->host, eventlog_lcase(computer), NTSL_SYS_LEN);
				strncpy0(event->source, eventlog_lcase(source), NTSL_SYS_LEN);
				eventlog_set_event_type(event, record->EventType);
				eventlog_set_event_msg(event, logType, record->EventID, strings, record->NumStrings);

				engine_process_event(event, NULL /*host->group*/);
				event = NULL;
			}
			bytes -= record->Length; 
			record = (EVENTLOGRECORD *) ((LPBYTE) record + record->Length); 
		}
 
        record = (EVENTLOGRECORD *)&buffer; 
    } 
 
    CloseEventLog(hLog); 
	return(0);
}


/*-------------------------[ eventlog_read_lastrun ]---------------------------
 *  Reads last run times
 *
 *	Returns:
 *		success		(0)
 *		error		(-1)	
 *----------------------------------------------------------------------------*/
static int eventlog_read_lastrun(uint32 *lastrun)
{
    char        buffer[REG_BUFFER_LEN];
    HKEY        hReg;
	int32		size = sizeof(*lastrun);
    int32       rc, rv;

	if (lastrun == NULL)
		return(-1);

    _snprintf0(buffer, REG_BUFFER_LEN, "SOFTWARE\\%s\\Syslog", DEV_NAME);
    rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hReg);
    if (rc != ERROR_SUCCESS)
            return(-1);

    rv  = RegQueryValueEx(hReg, LAST_RUN_REG, NULL, NULL, (unsigned char*)lastrun, &size);
    RegCloseKey(hReg);

    if ( (rv != ERROR_SUCCESS) || (*lastrun <0) )
	{
		*lastrun = 0;
	}	

	return(0);
}


/*--------------------------[ eventlog_write_lastrun ]-----------------------
 *  Writes last run time
 *
 *	Returns:
 *		success		(0)
 *		error		(-1)	
 *----------------------------------------------------------------------------*/
static int eventlog_write_lastrun(uint32 *lastrun)
{
    char        buffer[REG_BUFFER_LEN];
    HKEY        hReg;
    HKEY		hRegSub;
	int32		size = sizeof(*lastrun);
    int32       rc, rv;

	if (lastrun == NULL)
		return(-1);

    _snprintf0(buffer, REG_BUFFER_LEN, "SOFTWARE\\%s\\Syslog", DEV_NAME);
    rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, buffer, 0, KEY_SET_VALUE, &hReg);
    if (rc != ERROR_SUCCESS)
            return(-1);

    rc = RegCreateKeyEx(hReg, LAST_RUN_REG, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hRegSub, &rv);
    rv  = RegSetValueEx(hReg, LAST_RUN_REG, 0, REG_DWORD, (unsigned char*)lastrun, size);
    RegCloseKey(hReg);

    if (rv != ERROR_SUCCESS)
		ntsl_die(NTSL_ERROR_TIME_DATA_WRITE, LAST_RUN_REG);

	return(0);
}


/*--------------------------[ eventlog_check_events ]--------------------------
 * Locate recent event entries to be processed
 *----------------------------------------------------------------------------*/
void eventlog_check_events()
{
	int		running = 1;	
	uint32 	lastrun;
	uint32 	thisrun;

	time(&thisrun);

	if (eventlog_read_lastrun(&lastrun) == 0)
	{
		if (lastrun > 0)
		{
			if (eventlog_system)
				eventlog_read_events(&eventlog_system, "System", &lastrun, &thisrun);
			if (eventlog_security)
				eventlog_read_events(&eventlog_security, "Security", &lastrun, &thisrun);
			if (eventlog_application)
				eventlog_read_events(&eventlog_application, "Application", &lastrun, &thisrun);
		}

		if (running)
			eventlog_write_lastrun(&thisrun);
	}
}


/*----------------------------[ eventlog_read_reg ]----------------------------
 * Read eventlog registry settings 
 *----------------------------------------------------------------------------*/
static void eventlog_read_reg(int *eventlog, char *name)
{
    char        buffer[REG_BUFFER_LEN];
	int			val;
    HKEY        hReg;
	int32		size = sizeof(val);
    int32       rc, rv;

    while(1)    // while loop used as control structure
    {
        _snprintf0(buffer, REG_BUFFER_LEN, "SOFTWARE\\%s\\Syslog\\%s", DEV_NAME, name);
        rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hReg);
        if (rc != ERROR_SUCCESS)
            break;

        rv  = RegQueryValueEx(hReg, "Information", NULL, NULL, (unsigned char*)&val, &size);
        if ( (rv == ERROR_SUCCESS) && (val) )
			*eventlog |= EVENTLOG_INFORMATION_FLAG;

        rv  = RegQueryValueEx(hReg, "Warning", NULL, NULL, (unsigned char*)&val, &size);
        if ( (rv == ERROR_SUCCESS) && (val) )
			*eventlog |= EVENTLOG_WARNING_FLAG;

        rv  = RegQueryValueEx(hReg, "Error", NULL, NULL, (unsigned char*)&val, &size);
        if ( (rv == ERROR_SUCCESS) && (val) )
			*eventlog |= EVENTLOG_ERROR_FLAG;

        rv  = RegQueryValueEx(hReg, "Audit Success", NULL, NULL, (unsigned char*)&val, &size);
        if ( (rv == ERROR_SUCCESS) && (val) )
			*eventlog |= EVENTLOG_AUDIT_SUCCESS_FLAG;

        rv  = RegQueryValueEx(hReg, "Audit Failure", NULL, NULL, (unsigned char*)&val, &size);
        if ( (rv == ERROR_SUCCESS) && (val) )
			*eventlog |= EVENTLOG_AUDIT_FAILURE_FLAG;

        break;
    }

	
    RegCloseKey(hReg);

    if (rc != ERROR_SUCCESS)
        ntsl_die(NTSL_ERROR_CONFIG_READ, buffer);
}


int eventlog_init()
{
	eventlog_read_reg(&eventlog_system, "System");
	eventlog_read_reg(&eventlog_security, "Security");
	eventlog_read_reg(&eventlog_application, "Application");

	return(1);
}
